#ifndef __PSE_CACHEER_HASH_H__
#define __PSE_CACHEER_HASH_H__
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
/*
#define _GNU_SOURCE

#include <stdlib.h>
#include <string.h>
*/

#define pse_hash_warn(msg)                                               \
    fprintf(stderr,                                             \
            "%s at line %d of file %s\n", msg, __LINE__, __FILE__)

/**
 * pse_hash_die() prints error message to STDERR, and exits with -1 code.
 */
#define pse_hash_die(msg)                                                        \
    do {                                                                \
        fprintf(stderr,                                                 \
                "%s at line %d of file %s\n",                           \
                msg, __LINE__, __FILE__);                               \
        exit(-1);                                                       \
    } while (0)

#define IO_BUF_SIZE (8192)

//#define HASH_SIZE 50000

typedef struct _list_t{
    char           *string;             /* key = string itself */
    size_t          string_len;         /* the length of string */
    void           *data;               /* for key && value pair*/
    size_t          data_len;          
    time_t          OldTimeOfAccess;    /* the cached object was first referenced*/
    time_t          NewTimeOfAccess;    /* the last reference to the cached object*/
    time_t          timeoffirst_access; /* time of first access. the time the
                                         * specific objects was cached
                                         */                                                
    time_t          age;
    int             match_count;
    bool            empty;              /* indicate whether the specific object cache
                                         * location is empty or not */
    
    struct _list_t *next;               /* When it occurs collision,
                                         * it will create next list_t
                                         * below it is for old version
                                         */
} list_t;

/* for hashtable struct */
typedef struct _hash_table_t{
    unsigned int      size;             /* the size of the table */
    unsigned int      item_counter;     /* the number of items in the hash table */
    pthread_mutex_t   lock;
    list_t          **table;            /* the table elements */
} hash_table_t;

class PseHashTable{
    public:
        hash_table_t *hash_table;
        unsigned int  size;
        int           expired_seconds;
        int           memory_usage;       
        PseHashTable();
        ~PseHashTable();        
        int     hash_init(unsigned int hash_size);
        list_t *hash_lookup(char *str, size_t str_len);
        int     hash_add(char *str, size_t str_len, 
                         void *data, size_t data_len);
        int     hash_update(char *str, size_t str_len,
                            void *data, size_t data_len);
        int     hash_delete(char *str, size_t str_len);
        int     hash_clear();
        int     hash_memory_usage(void);
        int     hash_free();
        bool    hash_dump_file(const char *filename);
        bool    hash_load_file(const char *filename);        
        bool    hash_search_in_file(const char *str, int str_len, 
                                    const char *filename);
    private:
        int           _hash_add_from_file(char *str, size_t str_len, void *data,
                                          time_t OldTimeOfAccess, time_t NewTimeOfAccess,
                                          time_t timeoffirst_access, time_t age, 
                                          int match_count, bool empty);                                      
};


/**
 *  Here are some good prime number choices.  It's important not to
 *  choose a prime number that is too close to exact powers of 2.
 *
 *  HASH_SIZE 103               // prime number < 128
 *  HASH_SIZE 229               // prime number < 256
 *  HASH_SIZE 467               // prime number < 512
 *  HASH_SIZE 977               // prime number < 1024
 *  HASH_SIZE 1979              // prime number < 2048
 *  HASH_SIZE 4019              // prime number < 4096
 *  HASH_SIZE 6037              // prime number < 6144
 *  HASH_SIZE 7951              // prime number < 8192
 *  HASH_SIZE 12149             // prime number < 12288
 *  HASH_SIZE 16231             // prime number < 16384
 *  HASH_SIZE 33493             // prime number < 32768
 *  HASH_SIZE 65357             // prime number < 65536
 */


/**
 * hash_init() input the maximum size of hash table which you want to
 * create.
 *
 * @param size       the hash table size.
 * @return           return the hash table object which you create.
 */
//hash_table_t *hash_init(unsigned int size);

/**
 * hash_lookup() checks the string which occur in the hash table before
 * you add.
 *
 * The str must be a NULL-terminated string.
 *
 * @param *hashtable the hash table which you created
 * @param *str       the input data which you want to check.
 * @param  str_len   length of 'str'
 * @return           return term list.
 */
//list_t *hash_lookup(hash_table_t *hashtable, char *str, size_t str_len);

/**
 * hash_add() adds the string to your created hash table. If the string
 * is in the hash table, this function will skip it.
 *
 * The *str must be a NULL-terminated string.
 *
 * @param *hashtable the hash table which you created
 * @param *str       the input data which you want to add.
 * @param  str_len   length of 'str'
 * @return           if succuess return non-zero value.
 */
/*
int hash_add(hash_table_t *hashtable, unsigned int docid,
             unsigned int str_pos, unsigned int ngram,
             char *str, size_t str_len, void *data);

int hash_update(hash_table_t *self, char *str, size_t str_len,
                void *data, size_t data_len);

int hash_delete(hash_table_t *self, char *str, size_t str_len);

int hash_clear(hash_table_t *self);
*/

/**
 * hash_free() frees the object which you created hash table and term
 * list.
 *
 * @param *hashtable the hash table which you created.
 * @return           if success returned 1, otherwise return 0.
 */
//int hash_free(hash_table_t *hashtable);

#endif
